<template>
	<div :style="[{width: MaxWidth + 'px', marginLeft: sideMargin + 'px', minHeight: '100vh', border: '1px solid #E9E9E9', 'border-top-width': 0, 'border-bottom-width': 0}]">
		<div :style="[{height: openSearch && !childIndex && dataList.length > 0 ? '108px' : '44px', width: '100%'}]">
			<div :style="[{top: 0 + 'px', left: sideMargin + 'px', right: sideMargin + 'px', height: openSearch && !childIndex && dataList.length > 0 ? '108px' : '44px'}]" class="center-column top-bottom custom-navbar">
				<div style="width: 100%;height: 44px;position: relative;" class="space-between">
					<div style="position: absolute;inset: 0;" class="center">
						<CaLabel :text="PageOptions.title || '请选择'" title/>
					</div>
					<IconFont type="back" style="margin-left: 20px;" size="20" @click="onBack(1)"/>
				</div>
				<div v-if="openSearch && !childIndex && dataList.length > 0" style="height: 64px;width: 100%;">
					<CaSearchBar v-if="keyword || pagination.total > 0" style="width: 100%;" @confirm="onSearch"/>
					<div v-if="pagination.total > 0" class="space-between" style="width: 90%;padding: 0 5%;font-size: 12px;color: #999;">
						<div>每页{{ pagination.pageSize }}条，共{{ pagination.pageNumber }}页，已加载{{ pagination.pageIndex + 1 }}页</div>
						<div style="display: flex;align-items: center;">
							<div>总数：{{ pagination.total }}条</div>
							<IconFont type="shuaxin" size="14" color="#999" style="margin-left: 5px;" @click="getDataList(true)"/>
						</div>
					</div>
				</div>
			</div>
		</div>
		<div style="width: 100%;">
			<uni-list v-if="(children || dataList).length > 0">
				<uni-list-item v-for="(record, index) in children || dataList" :key="index">
					<uni-group slot="body" v-if="option.typeDetail === 'section'" :title="record.text" style="width: 100%;" top="0" padding="0 15px">
						<div slot="right">
							<CaLabel v-if="record.children && record.children.length > 0" size="10" :text="record.is_spread ? '收起' : '展开'" @click="e => record.is_spread = !record.is_spread"/>
							<!-- <div v-else-if="option.copy" style="display: flex;align-items: center;">
								<CaLabel text="复制" size="12" :color="core.theme.color.click" cancelBubble style="margin-right: 10px;" @click="onCopy(record)"/>
								<IconFont v-if="record.select" type="dui"/>
							</div> -->
							<IconFont v-else type="dui"/>
						</div>
						<block v-if="record.is_spread">
							<div v-for="(item, idx) in record.children" :key="idx" class="space-between" style="border-bottom: 1px solid #E9E9E9;height: 36px;" @click="onSelect(record, index, item, idx)">
								<CaLabel :text="item.text"/>
								<div style="display: flex;align-items: center;">
									<CaLabel v-if="option.copy" text="复制" size="12" :color="core.theme.color.click" cancelBubble style="margin-right: 10px;" @click="onCopy(item)"/>
									<IconFont v-if="item.select" type="dui"/>
								</div>
							</div>
						</block>
					</uni-group>
					<div slot="body" v-else class="space-between" style="width: 100%;" @click="onSelect(record, index)">
						<CaLabel :text="record.text"/>
						<div style="display: flex;align-items: center;">
							<CaLabel v-if="option.copy" text="复制" size="12" :color="core.theme.color.click" cancelBubble style="margin-right: 10px;" @click="onCopy(record)"/>
							<IconFont v-if="record.select" type="dui"/>
						</div>
					</div>
				</uni-list-item>
			</uni-list>
			<LoadView v-else loading="noData"/>
			<LoadView v-if="loadStatus === 'params'" :loading="loadStatus" text="请从上个页面进入"/>
			<LoadView v-else-if="!childIndex" :loading="loadStatus" @click="onLoadStatus"/>
		</div>
		<div v-if="option.isMultiple" style="height: 64px;width: 100%;">
			<div :style="[{bottom: 0, left: sideMargin + 'px', right: sideMargin + 'px'}]" class="center top-bottom">
				<div v-if="childIndex" class="space-between" style="width: 100%;padding: 0 5%;">
					<CaButton title="前页" @click="onPrefix"/>
					<div style="flex: 1">
						<CaLabel :text="`已选择${dbValue.length}项`" size="12"/>
						<CaLabel :text="showDbArr().join('、')"/>
					</div>
					<CaButton title="确定" type="primary" @click="onBack(2)"/>
				</div>
				<CaButton v-else title="确定" style="width: 80%;" type="primary" @click="onBack(2)"/>
			</div>
		</div>
	</div>
</template>

<script>
	
	import base from '@/pages/index.vue'
	// 该页面限定最大宽度800
	const MaxWidth = 800
	
	/**
	 * 数据选择。支持单选多选以及级联选择
	 * 必须是push的方式进入，并且传入要选择的数据，选择完数据后自动返回上个页面，并执行回调参数
	 */
	
	export default {
		extends: base,
		data() {
			return {
				MaxWidth, sideMargin: 0,
				childIndex: 0, // 是否是子页面详情选择。大于0表示子页面详情选择，将没有搜索框，并且不显示分页信息
				children: undefined, // 子页面详情选择的数据
				multiple: [], // 多选时记录层级选择，返回时知道显示那些数据
				
				openSearch: true, // 是否打开搜索。默认都可以搜索
				keyword: '', // 搜索的关键字
				dbValue: [], // 已经选择的值，从准确性上看，应该全部转化为数组
				
				// 实际使用的配置参数
				option: { // 配置参数。参数等同于db-dataselector的info。但是实际用得着的项不多
					copy: false, // 是否显示复制值按钮
					rankIndex: -1, // 级联选择的层级。大于等于0，表示跳转到页面进行选择：0表示只选择第一层，等同于该组件；大于0，表示级联选择，默认选择到第几层
					typeDetail: '', // section：表示在一个页面内分组
					
					isMultiple: false, // 是否多选
					minLength: '', // 多选时最少选几个
					maxLength: '', // 多选时最多选几个
					description: '',
					placeholder: '',
					clearable: true, // 是否显示清除按钮
					/**
					 * 筛选的选项有
					 * 本地数据：enum
					 * 云服务空间数据：tableName和spaceName以及dataParams和pageSize
					 * 请求链接：dataUrl和dataParams和pageSize
					 * 优先级：dataUrl > tableName > enum
					 */
					enum: '', // 枚举的本地数据
					spaceName: '', // 服务空间名字
					tableName: '', // 请求的数据库
					selfField: '', // 当前值字段，可用作map的value字段名
					nameKey: '', // 表示名称的字段，可用作map的text字段名
					itemAction: undefined, // 查询到结果后，对每一项的处理，一般处理是否可选：disable
					parentKey: '', // 当前表字段。进入这里之前已经获取了tableName，该字段实际就是selfField
					parentValue: '', // 当前表字段的当前值。该值存在则作为要排除自身以及自身所有下级的依据！
					foreignKey: '', // 关联表信息。可拆分为tableName和selfField。进入这里前已经被转化了，所以该字段在这里已无效
					dataParams: undefined, // 查询的参数
					dataUrl: '', // 请求的url链接
					pageSize: 20, // 每页几个，默认20个
					
					map: {text: '', value: '', disable: 'disable'}, // 映射规则。搜索的话，字段识别是text
					format: '', // 针对原始数据的格式化输出（map的text映射规则无效）。比如{name} - {phone}。显示为“姓名 - 电话”。如果搜索关键字识别第一个括号内的字段
					formatAction: undefined, // 自定义的映射规则方法回调。可针对固定数据再做特殊处理，依赖于format。回调值为（key, data）=> {return data[key]}。必须依赖于format而存在
					
					emptyTips: '', // 默认“没有数据”
					moreText: '', // 默认“更多数据”
				},
				
				dataList: [], // 用于显示的数据
				sourceData: [], // 原始数据
				pagination: { // 分页信息
					pageIndex: 0,
					pageNumber: 1,
					pageSize: 20,
					total: 0
				},
				loadStatus: 'params', // 加载状态。默认params：参数错误，等拿到路由参数后根据参数结果初始化
			}
		},
		onBackPress() { // 执行了返回都会走这里
			// this.onBack(0)
		},
		onPullDownRefresh() {
			
		},
		onReachBottom() {
			if (this.loadStatus === 'more') {
				this.getDataList(false)
			}
		},
		methods: {
			initial({option = {}, dbValue = []} = this.PageOptions) {
				if (this.deviceInfo.windowWidth > MaxWidth) { // 计算左右边距，使页面居中
					this.sideMargin = (this.deviceInfo.windowWidth - MaxWidth) * 0.5
				}
				var {pageSize, typeDetail, isMultiple, rankIndex, url, spaceName, tableName, nameKey, selfField, parentKey, dataParams = {}} = option
				if (!Array.isArray(dbValue)) {
					dbValue = [dbValue]
				}
				this.dbValue = dbValue
				if (pageSize) {
					this.pagination.pageSize = pageSize
				} else {
					if (this.deviceInfo.windowHeight > 1000) {
						this.pagination.pageSize = parseInt(this.deviceInfo.windowHeight / 400) * 10
					}
				}
				if (url || tableName || option.enum) {
					this.option = option
					
					// option.enum = undefined
					// tableName = 'car' // 可能数据量太大，查询报未知错误：Database error。替换为opendb-city-china没问题
					// nameKey = 'name'
					// selfField = 'code'
					// parentKey = 'parent_code'
					// dataParams.isSaling = true
					// option.map = {text: 'name', value: 'code'}
					
					// 级联选择的数据需先查询完毕后给option.enum赋值再执行
					if (!option.enum && rankIndex > 0 && tableName && nameKey && selfField) {
						dataParams.field = `${nameKey},${selfField}`
						dataParams.parentKey = parentKey
						dataParams.getTree = {limitLevel: rankIndex}
						this.loadStatus = 'loading'
						this.core.request.get({space: spaceName, name: tableName}, dataParams, {pageSize: 1000, pageIndex: 0}, true).then(res => {
							this.loadStatus = ''
							option.enum = res.data
							this.getDataList(true)
						}).catch(err => {
							this.loadStatus = 'error'
						})
					} else {
						this.getDataList(true)
					}
				} else {
					this.loadStatus = 'params'
				}
			},
			
			/**
			 * 初始化已选择的数据
			 * 确定哪些数据已经被选择了
			 * 如果内层数据被选择，那么外层数据也必须被选择
			 */
			initialValue() {
				// 循环遍历。array：要遍历的数组, index：该数组所在对象的索引值，i第几层
				const cycle = (array, i = 0) => {
					// 是否有一个被选择。只要有一个被选择，就会告诉上层被选择了
					var hasSelect = false
					array.forEach((item, childIndex) => {
						var isSelect = this.dbValue.includes(item.value) 
						if (item.children) {
							const res = cycle(item.children, i + 1)
							if (res) {
								isSelect = res
							}
						}
						if (isSelect) { // 当前层确定被选择了
							item.select = true
							hasSelect = true
						} else {
							item.select = false
						}
					})
					return hasSelect
				}
				if (this.dbValue && this.dbValue.length > 0) {
					cycle(this.dataList)
				}
			},
			
			onLoadStatus() {
				if (this.loadStatus === 'more') {
					this.getDataList(false)
				}
			},
			
			// 搜索
			onSearch(e) {
				this.keyword = e.keyword
				this.getDataList(true)
			},
			
			/**
			 * 获取数据。网络请求的话，支持单页无限查询。级联选择的数据需提前准备好，当做是本地数据
			 * refresh：是否第一页数据
			 * callback：得到数据后的回调
			 * 判断已选择的数据是否符合，符合的话，设置选择索引
			 */
			getDataList(refresh, callback)  {
				if (this.loadStatus.startsWith('loading')) {
					return
				} else {
					this.loadStatus = refresh ? 'loading' : 'loading-list'
				}
				if (refresh) {
					this.pagination.pageIndex = 0
					this.dataList.length = 0
					this.sourceData.length = 0
				} else {
					if (this.pagination.pageIndex < this.pagination.pageNumber) {
						this.pagination.pageIndex += 1
					}
				}
				const {pageSize} = this.pagination
				var {tableName, spaceName, selfField, dataUrl, dataParams = {}, isMultiple, format, map, formatAction, itemAction} = this.option
				dataParams = this.core.synctool.copy.deep(dataParams)
				// 请求成功后的处理，url要转化为与cloud一样的结果
				const success = (res) => {
					if (res) {
						if (res.pagination) {
							this.pagination = res.pagination
						}
						if (res.data.length === 0) {
							if (this.pagination.pageIndex > 0) {
								this.pagination.pageIndex -= 1
							}
						} else {
							
							// 格式化输出一个数据
							const formatAction = (item) => {
								const obj = {
									text: this.core.synctool.regexp.getFormatText(item, format, map, formatAction),
									value: map ? item[map.value] : item.value,
									disable: map ? item[map.disable] : (item.disable || item.remove),
									select: this.dbValue.includes(item.value),
									is_spread: true
								}
								itemAction && itemAction(obj, item)
								if (item.children) {
									item.children.forEach(child => {
										const childObj = formatAction(child)
										if (!obj.children) {
											obj.children = []
										}
										obj.children.push(childObj)
									})
								}
								return obj
							}
							
							res.data.map(item => {
								this.sourceData.push(item)
								// 格式化输出一个数据
								const obj = formatAction(item)
								this.dataList.push(obj)
							})
						}
						this.initialValue()
						this.loadStatus = res.data.length === this.pagination.pageSize ? 'more' : ''
					} else {
						this.loadStatus = ''
					}
					callback && callback()
				}
				
				// 请求失败
				const fail = (err) => {
					this.loadStatus = 'error'
					if (this.pagination.pageIndex > 0) {
						this.pagination.pageIndex -= 1
					}
				}
				
				this.pagination.pageSize = pageSize
				// 关键字
				if (this.keyword) {
					// 取format第一个值，或者map.text
					const valueKey = format ? format.split('}')[0].split('{')[1] : map ? map.text : 'text'
					dataParams[valueKey] = {
						filterType: 'search',
						filter: this.keyword
					}
				}
				const config = {cache: 100}
				const {pageIndex} = this.pagination
				if (dataUrl) { // 在url后面拼页码参数
					// 以下仅是示例。需根据实际url的情况来处理pagination和dataParams
					const url = dataUrl + `?pageSize=${pageSize}&pageIndex=${pageIndex}`
					this.core.request.fetchUrl(url, dataParams, false, config).then(res => {
						success(res)
					}).catch(fail)
				} else if (tableName) { // 云对象查询数据库
					// 默认只查询text和value字段
					const fields = []
					fields.push(map.text)
					if (!fields.includes(map.value)) {
						fields.push(map.value)
					}
					if (format) {
						// 提取{}内的key
						const regex = /\{([^}]+)\}/g;
						let match;
						while ((match = regex.exec(str))!== null) {
							keys.push(match[1]);
							if (!fields.includes(match[1])) {
								fields.push(match[1])
							}
						}
					}
					dataParams.field = fields.join(',')
					this.core.request.get({space: spaceName, name: tableName}, dataParams, this.pagination, false, config).then(res => {
						success(res)
					}).catch(fail)
				} else { // 本地数据
					const dataList = [] // 满足条件的数据
					if (this.option.enum) {
						if (this.keyword) {
							// 内部含有都算
							const cycle = (enumObj) => {
								if (enumObj[map.text].includes(this.keyword)) {
									return true
								} else {
									if (enumObj.children) {
										for (let i = 0; i < enumObj.children.length; i++) {
											if (cycle(enumObj.children[i])) {
												return true
											}
										}
									}
								}
								return false
							}
							for (let i = 0; i < this.option.enum.length; i++) {
								if (cycle(this.option.enum[i])) {
									dataList.push(this.option.enum[i])
								}
							}
						} else {
							this.option.enum.map(item => {
								dataList.push(item)
							})
						}
					}
					const arr = [] // 本次拿到的数据
					for (let i = 0; i < pageSize; i++) {
						if (pageSize * pageIndex + i < dataList.length) {
							arr.push(dataList[pageSize * pageIndex + i])
						}
					}
					let pageNumber = parseInt(dataList.length / pageSize)
					if (pageNumber * pageSize < dataList.length) {
						pageNumber += 1
					}
					success({data: arr, pagination: {
						pageIndex, pageSize, total: dataList.length, pageNumber
					}})
				}
			},
			
			/**
			 * 选择了某一项
			 * item, idx：可能存在，存在的话，由它代替record和index
			 */
			onSelect(record, index, item, idx) {
				// 记录层级选择
				if (this.childIndex === 0) {
					this.multiple = [index]
				} else {
					this.multiple.push(index)
				}
				if (item) {
					this.multiple.push(idx)
					record = item
					index = idx
				}
				const {rankIndex, typeDetail, isMultiple} = this.option
				if (rankIndex > this.childIndex + (typeDetail === 'section' ? 1 : 0)) {
					// 还可以进一步分层
					if (record.children && record.children.length > 0) {
						this.children = record.children
						this.childIndex += 1
						return
					} else {
						// 无法再进一步，就选择当前项
					}
				}
				/**
				 * 处理选择结果并记录选择的索引
				 */
				record.select = !record.select
				if (isMultiple) {
					// 多选，需要点确定并返回
					const res = this.dbValue.indexOf(record.value)
					if (res < 0) {
						this.dbValue.push(record.value)
					} else {
						this.dbValue.splice(res, 1)
					}
					this.initialValue()
				} else { // 单选直接完成选择
					this.dbValue = [record.value]
					this.onBack(2)
				}
			},
			
			// 点击了复制值
			onCopy(item) {
				this.core.setClipboardData(item.value)
			},
			
			// 点击了多选的前页按钮
			onPrefix() {
				if (this.childIndex > 0) {
					this.multiple.pop()
					this.childIndex -= 1
					if (this.childIndex === 0) {
						this.children = undefined
					} else {
						// 找到对应的children
						var array = this.dataList
						this.multiple.forEach(index => {
							array = array[index].children
						})
						this.children = array
					}
				}
			},
			
			/**
			 * 点击返回
			 * 单选，在最终的选择项上直接执行。多选需回到childIndex=0，然后执行onBack
			 * 也可以返回前一页
			 * type：返回类型
			 * 	0：系统返回
			 * 	1：手动返回
			 * 	2：选择了内容后的返回
			 */
			onBack(type = 2) {
				const {childIndex} = this
				if (type === 1 && childIndex > 0) {
					this.childIndex -= 1
					if (this.childIndex === 0) {
						this.children = undefined
					}
				} else {
					var value = this.dbValue
					var dbText = this.showDbArr()
					var dbOptions = this.showDbArr(true)
					if (!this.option.isMultiple) {
						value = value.length > 0 ? value[0] : ''
						dbText = dbText.length > 0 ? dbText[0] : ''
					}
					this.PageOptions.callback && this.PageOptions.callback({
						dbValue: value, dbText, dbOptions
					})
					if (type > 0) { // 需要手动执行返回
						this.core.router.back()
					}
				}
			},
			
			// 显示已经选择的项
			showDbArr(forRes) {
				const arr = [], valueKey = (forRes && this.option.map ? this.option.map.value : '') || 'value'
				const cycle = (array) => {
					array && array.forEach(item => {
						if (this.dbValue.includes(item[valueKey])) {
							if (forRes) {
								arr.push(item)
							} else {
								arr.push(item.text)
							}
						}
						cycle(item.children)
					})
				}
				cycle(forRes ? this.sourceData : this.dataList)
				return arr
			}
		}
	}
</script>

<style>
	.top-bottom {
		position: fixed;
		left: 0;right: 0;height: 64px;
		background-color: white;
		z-index: 1;
	}
	
	.page-wrapper {
	  display: flex;
	  flex-direction: column;
	  align-items: center;
	}
	
	.custom-navbar {
	  display: flex;
	  justify-content: center;
	  background-color: #fff;
	  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
	}
</style>
